#include "main.h"
#include "freertos/FreeRTOS.h"
#include "math.h"
#include "string.h"
#include "time.h"
#include "esp_log.h"
esp32_t esp32;

// modbus register changed callback
void relay_control_cb(uint16_t *args);
void relay_heater_cb(uint16_t *args);
void relay_jpump_cb(uint16_t *args);
void relay_main_cb(uint16_t *args);
void relay_up_cb(uint16_t *args);
void relay_down_cb(uint16_t *args);
void relay_release_cb(uint16_t *args);
void relay_back_cb(uint16_t *args);
void slave485_register_init();
void mpump_cb(uint16_t *args);

void master485_send(uint8_t *data, int len, void *user)
{
    bsp_uart_a_tx(data, len, save_last_cmd, user);
}
uint16_t mpump_reg_value[7]; // mpump_operate_value, mpump_status_value, mpump_voltage_value, mpump_current_value,
                             // mpump_r_speed_value, mpump_temperature_value};
void temp(ads1115_channel_t ch)
{
    for (int i = 0; i < ADS1115_NUM; i++)
    {
        if (ads1115[i] != NULL)
        {
            ads1115_oneshot_chx(ads1115[i], ch);
        }
    }
    for (int i = 0; i < ADS1115_NUM; i++)
    {
        if (ads1115[i] != NULL)
        {
            ads1115_read(ads1115[i]);
        }
    }
}

void uart_a_rx_cb(uint8_t *data, uint16_t size)
{
    printf("UART A received: ");
    for (int i = 0; i < size; i++)
    {
        printf("%02x", data[i]);
    }
    printf("\n");
}

void uart_b_rx_cb(uint8_t *data, int size)
{
    printf("UART B received: ");
    for (int i = 0; i < size; i++)
    {
        printf("%02X ", data[i]);
    }
        printf("\n");
    if (data[0] == DEV_ADR)
    {
        if (data[1] == 0x03)
        {
            uint16_t addr = (data[2] << 8) | data[3];
            uint16_t quantity = (data[4] << 8) | data[5];
            uint16_t *buf = malloc(sizeof(uint16_t) * quantity + 5);
            uint8_t *buf_b = (uint8_t *)buf;
            buf_b[0] = DEV_ADR;
            buf_b[1] = 0x03;
            buf_b[2] = (quantity * 2) & 0xFF;
            slave485_read_regs(&slave485, addr, (uint16_t *)(buf_b + 3), quantity);
            // change byte order
            uint8_t *cbx = buf_b + 3;
            for (int i = 0; i < quantity; i++)
            {
                uint8_t temp = cbx[i * 2];
                cbx[i * 2] = cbx[i * 2 + 1];
                cbx[i * 2 + 1] = temp;
            }
            // CRC
            uint16_t crc = crc16(buf_b, 3 + 2 * quantity, 0x8005);
            buf_b[3 + 2 * quantity] = (uint8_t)(crc & 0xFF);
            buf_b[4 + 2 * quantity] = (uint8_t)(crc >> 8);
            bsp_uart_b_tx((uint8_t *)buf, quantity * 2 + 5);
            free(buf);
        }
        else if (data[1] == 0x06)
        {
            uint16_t addr = (data[2] << 8) | data[3];
            uint16_t value = (data[4] << 8) | data[5];
            slave485_write_regs(&slave485, addr, &value, 1);
            // CRC
            bsp_uart_b_tx(data, 8);
        }
    }
    // printf("\n");
}

void mpump_handler(master485_t *master485)
{
    // master485_err_t master485_err;
    // check

    // master485_read_regs(master485, MPUMP_ADR, mpump_reg[0], 1, 0);
    // vTaskDelay(500 / portTICK_PERIOD_MS);
    master485_read_regs(master485, MPUMP_ADR, MPUMP_DATA_STATUS_ADR, 4, 1);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    master485_read_regs(master485, MPUMP_ADR, MPUMP_TEMPERATURE_ADR, 1, 5);
    vTaskDelay(1000 / portTICK_PERIOD_MS);

    for (int i = 1; i < 6; i++)
    {
        master485_get_value_frome_mem(master485, MPUMP_ADR, true, i, &mpump_reg_value[i]);
    }
    slave485_write_regs(&slave485, ESP_MPUMP_DATA_STATUS_ADR, &mpump_reg_value[1], 4);
    vTaskDelay(100 / portTICK_PERIOD_MS);

    slave485_write_regs(&slave485, ESP_MPUMP_TEMPERATURE_ADR, &mpump_reg_value[5], 1);
    vTaskDelay(100 / portTICK_PERIOD_MS);
}

// this function is called when the app starts
// and is used to initialize the peripherals, will be called only once
void app_main(void)
{
    io_init();
    i2c_device_tree_init();
    ads1115_init(&i2c_dev_lst);
    // dac8571_init(&i2c_dev_lst);
    // vTaskDelay(5000 / portTICK_PERIOD_MS);
    // master485
    master485_err_t master485_err;
    master485_err = master485_init(&esp32.master485, master485_send);
    master485_err = master485_add_dev(&esp32.master485, 0x10, 7, 0, 2000); // mpump
    if (master485_err != MASTER485_OK)
        ESP_LOGE("MASTER_485", "ERR:%d", master485_err);
    io_set_level(MIOO_VMAP3_CTL, HIGH);
    io_set_level(MIOO_HEVRUN, HIGH);
    io_set_level(MIOO_VPVCTL, HIGH);
    io_set_level(MIOO_HEVCTL, HIGH);

    uart_a_tx_semophore = xSemaphoreCreateBinary();
    uart_a_tx_list_mutex = xSemaphoreCreateBinary();
    xSemaphoreGive(uart_a_tx_semophore);
    xSemaphoreGive(uart_a_tx_list_mutex);
    uart_b_tx_list_mutex = xSemaphoreCreateBinary();
    xSemaphoreGive(uart_b_tx_list_mutex);

    bsp_uart_a_init(9600);
    xTaskCreate(uart_a_rx_task, "uart_a_rx_task", 1024 * 4, &esp32.master485, 1, &uart_a_rx_task_handle);
    xTaskCreate(uart_a_tx_task, "uart_a_tx_task", 1024 * 4, NULL, 1, &uart_a_tx_task_handle);
    bsp_uart_a_register_rx_cb((void *)master485_receive);

    bsp_uart_b_init(9600);
    bsp_uart_b_register_rx_cb(uart_b_rx_cb);

    // open relay power
    relay_module_ope(true);

    // dac8571_set_value(dac8571[0], 0x7FFF);

    // char a = 'P';
    slave485_init(&slave485, 0x50, 9600);

    // init slave regs
    slave485_register_init();

    // for (int i = 0; i < 10; i++)
    // {
    //     char buf[100];
    //     sprintf(buf, "Hello, this is UART A %d times sending\n", i);
    //     bsp_uart_a_tx((uint8_t *)buf, strlen(buf), 500, 0);
    //     sprintf(buf, "Hello, this is UART B %d times sending\n", i);
    //     bsp_uart_b_tx((uint8_t *)buf, strlen(buf), 500, 0);
    // }

    while (1)
    {
        mpump_handler(&esp32.master485);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    //     temp(ads_ch0_gnd);
    //     temp(ads_ch1_gnd);
    //     temp(ads_ch2_gnd);
    //     temp(ads_ch3_gnd);

    //     // for (int i = 0; i < ADS1115_NUM; i++)
    //     // {
    //     //     if (ads1115[i] != NULL)
    //     //     {
    //     //         printf("ADS %d voltage V1: %.3f, V2: %.3f, V3: %.3f, V4: %.3f\n", i, ads1115[i]->vlotage[0], ads1115[i]->vlotage[1], ads1115[i]->vlotage[2], ads1115[i]->vlotage[3]);
    //     //     }
    //     // }
    //

    //     slave485_write_regs(&slave485, 0x00A1, value, 2);

    //     // printf("VAC: %1.1E Pa\n", vac);
    //     // uart_write_bytes(UART_B, "Hello World!\n", 13);
}

void slave485_register_init()
{
    // Relay register
    slave_reg_t reg = {
        .adr = 0x0020,
        .size = 1,
        .cb = relay_heater_cb};
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0021;
    reg.size = 1;
    reg.cb = relay_jpump_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0022;
    reg.size = 1;
    reg.cb = relay_main_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0023;
    reg.size = 1;
    reg.cb = relay_up_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0024;
    reg.size = 1;
    reg.cb = relay_down_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0025;
    reg.size = 1;
    reg.cb = relay_release_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = 0x0026;
    reg.size = 1;
    reg.cb = relay_back_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = ESP_MPUMP_OPERATE_ADR;
    reg.size = 1;
    reg.cb = mpump_cb;
    slave485_add_reg(&slave485, &reg);

    reg.adr = ESP_MPUMP_DATA_STATUS_ADR;
    reg.size = 4;
    reg.cb = NULL;
    slave485_add_reg(&slave485, &reg);

    reg.adr = ESP_MPUMP_TEMPERATURE_ADR;
    reg.size = 1;
    reg.cb = NULL;
    slave485_add_reg(&slave485, &reg);
    // 10 06 20 00 00 08 start
    // 10 06 20 00 00 03 close
    // 10 06 20 00 00 02 low
    // 10 06 20 00 00 01 high
    // 10 03 10 00 00 04 status
    // 10 03 10 04 00 01 temperature

    // // ports outputs control
    // reg.adr = 0x0011;
    // reg.size = 1;
    // reg.cb = NULL;
    // slave485_add_reg(&slave485, &reg);

    // // ports connect status
    // reg.adr = 0x00A0;
    // reg.size = 1;
    // reg.cb = NULL;
    // slave485_add_reg(&slave485, &reg);

    // // main vaacumeter valve
    // reg.adr = 0x00A1;
    // reg.size = 2;
    // reg.cb = NULL;
    // slave485_add_reg(&slave485, &reg);
}

void relay_control_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    for (int i = 0; i < fmin(RLY_NUM, 8); i++)
    {
        if (rlys & (1 << i))
        {
            rly_set(i, RLY_ON);
        }
        else
        {
            rly_set(i, RLY_OFF);
        }
    }
}

void relay_heater_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_HEATER, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_HEATER, RLY_OFF);
}
void relay_jpump_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_JUMP, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_JUMP, RLY_OFF);
}
void relay_main_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_MAIN, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_MAIN, RLY_OFF);
}
void relay_up_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_UP, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_UP, RLY_OFF);
}
void relay_down_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_DOWN, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_DOWN, RLY_OFF);
}
void relay_release_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_RELEASE, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_RELEASE, RLY_OFF);
}
void relay_back_cb(uint16_t *args)
{
    uint16_t rlys = *args;
    if (rlys == 0x0001)
        rly_set(RELAY_BACK, RLY_ON);
    else if (rlys == 0x00FF)
        rly_set(RELAY_BACK, RLY_OFF);
}

void mpump_cb(uint16_t *args)
{
    // 10 06 20 00 00 08 start
    // 10 06 20 00 00 03 close
    // 10 06 20 00 00 02 low
    // 10 06 20 00 00 01 high
    uint16_t ctl = *args;
    if (ctl == 0x0003)
        master485_write_reg(&esp32.master485, MPUMP_ADR, MPUMP_OPERATE_ADR, 0x0003, 0); // send 10 06 20 00 00 03
    else if (ctl == 0x0008)
        master485_write_reg(&esp32.master485, MPUMP_ADR, MPUMP_OPERATE_ADR, 0x0008, 0); // send 10 06 20 00 00 08
    else if (ctl == 0x0001)
        master485_write_reg(&esp32.master485, MPUMP_ADR, MPUMP_OPERATE_ADR, 0x0001, 0); // send 10 06 20 00 00 01
    else if (ctl == 0x0002)
        master485_write_reg(&esp32.master485, MPUMP_ADR, MPUMP_OPERATE_ADR, 0x0002, 0); // send 10 06 20 00 00 02
}